package com.dayu.finecomm.handler.tpacket;

import java.net.InetSocketAddress;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.dayu.finecomm.encoder.PacketEncoder;
import com.dayu.finecomm.handler.WriteAndCloseHandler;
import com.dayu.finecomm.pojo.DeviceConfig;
import com.dayu.finecomm.pojo.DevicePusher;
import com.dayu.finecomm.protocol.SpecialCareChannelInboundAdapter;
import com.dayu.finecomm.protocol.pojo.TPacket;
import com.dayu.finecomm.source.PushSource;

import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.DatagramPacket;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioDatagramChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GenericFutureListener;

public final class TPacketPushHandler extends SpecialCareChannelInboundAdapter<TPacket> {

	static Logger LOGGER = LoggerFactory.getLogger(TPacketPushHandler.class);

	final PushSource source;

	Bootstrap BOOTSTRAP;
	Bootstrap BOOTSTRAPUDP;

	public TPacketPushHandler(PushSource source, DeviceConfig ctx) {

		this.source = source;

		if (source.getPushers().size() > 0) {

			boolean tcp = false;

			for (DevicePusher pusher : source.getPushers()) {
				tcp = tcp || "tcp".equals(pusher.type);
			}

			if (tcp) { // tcp

				BOOTSTRAP = new Bootstrap();
				BOOTSTRAP.group(new NioEventLoopGroup()).channel(NioSocketChannel.class)
						.handler(new ChannelInitializer<SocketChannel>() {

							@Override
							public void initChannel(final SocketChannel ch) throws Exception {
								ch.pipeline().addLast(new WriteAndCloseHandler());
							}
						});

				LOGGER.info(String.format("Construct pusher-handler for %d target.", source.getPushers().size()));

			} else {// udp

				BOOTSTRAPUDP = new Bootstrap();
				BOOTSTRAPUDP.group(new NioEventLoopGroup()).channel(NioDatagramChannel.class)
						.handler(new ChannelInitializer<NioDatagramChannel>() {

							@Override
							public void initChannel(final NioDatagramChannel ch) throws Exception {
								ch.pipeline().addLast(new WriteAndCloseHandler());
							}
						});

				LOGGER.info(String.format("Construct pusher-handler for %d target.", source.getPushers().size()));
			}
		}
	}

	@Override
	public void channelRead0(ChannelHandlerContext ctx, final TPacket msg) throws Exception {

		for (final DevicePusher pusher : source.getPushers()) {

			switch (pusher.type.toLowerCase()) {
			case "tcp":
				for (ByteBuf s : PacketEncoder.encode(msg, pusher.format)) {
					push(s, pusher);
				}
				break;

			case "udp":

				for (ByteBuf s : PacketEncoder.encode(msg, pusher.format)) {
					pushUdp(s, pusher);
				}
				break;
			}
		}

		ctx.fireChannelRead(msg);
	}

	/**
	 * udp转发
	 */
	private void pushUdp(final ByteBuf msg, final DevicePusher pusher) {

		final ChannelFuture cf = BOOTSTRAPUDP.connect(pusher.host, pusher.port);

		cf.addListener(new GenericFutureListener<Future<? super Void>>() {

			@Override
			public void operationComplete(Future<? super Void> future) throws Exception {

				cf.channel().writeAndFlush(new DatagramPacket(msg, new InetSocketAddress(pusher.host, pusher.port)));
			}
		});
	}

	/**
	 * tcp转发
	 */
	private void push(final ByteBuf msg, final DevicePusher pusher) {

		final ChannelFuture cf = BOOTSTRAP.connect(pusher.host, pusher.port);

		cf.addListener(new GenericFutureListener<Future<? super Void>>() {

			@Override
			public void operationComplete(Future<? super Void> future) throws Exception {

				cf.channel().write(msg);
			}
		});
	}

	public static class TPacketPostData {

		public String method;

		public String payload;

		public TPacketPostData() {
		}

		public TPacketPostData(String method, String payload) {

			this.method = method;
			this.payload = payload;
		}

	}
}